//#include "tssynapse.h"
#include "tsdna.h"
#include <stdlib.h>
#include <glib-object.h>

typedef struct _TsSynapse {
  GObject parent_instance;

  gint64 id;  //space
  gint64 value;
  gint64 proportion;
  gint64 dendrite_id;

//  gint64 axon;
  gint64 time;
  gint64 type;//1 dendrite; 0 axon
  //gint64 checksum;

  gboolean status;

  gchar *name;

  GList *peer_list;

  gpointer dna;

  gpointer parent;
  gpointer son;
  gpointer next;
} TsSynapse;
typedef struct _TsSynapseClass {
    GObjectClass parent_class;
    void (*value_changed) (TsSynapse *self, int value);
} TsSynapseClass;
enum {
  PROP_0,
  PROP_ID, PROP_VALUE, PROP_PROPORTION, PROP_DENDRITE_ID,
  PROP_TIME, PROP_TYPE,
  PROP_STATUS,
  PROP_NAME,
  PROP_PEER_LIST,
  PROP_DNA,
  PROP_PARENT, PROP_SON, PROP_NEXT,
  N_PROPERTIES };

static GParamSpec *properties[N_PROPERTIES] = { NULL, };

//typedef struct _TsSynapse TsSynapse;
//typedef struct _TsSynapseClass TsSynapseClass;
G_DEFINE_TYPE (TsSynapse, ts_synapse, G_TYPE_OBJECT)

static void
ts_synapse_set_id (TsSynapse *obj,
                     gint64        id)
{
  if (obj->id != id)
    {
      obj->id = id;

      g_assert (properties[PROP_ID] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_ID]);
    }
}
static void
ts_synapse_set_value (TsSynapse *obj,
                     gint64        value)
{
  if (obj->value != value)
    {
      obj->value = value;

      g_assert (properties[PROP_VALUE] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_VALUE]);
    }
}
static void
ts_synapse_set_proportion (TsSynapse *obj,
                     gint64        value)
{
  if (obj->proportion != value)
    {
      obj->proportion = value;

      g_assert (properties[PROP_PROPORTION] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_PROPORTION]);
    }
}
static void
ts_synapse_set_dendrite_id (TsSynapse *obj,
                     gint64        value)
{
  if (obj->dendrite_id != value)
    {
      obj->dendrite_id = value;

      g_assert (properties[PROP_DENDRITE_ID] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_DENDRITE_ID]);
    }
}
static void
ts_synapse_set_time (TsSynapse *obj,
                     gint64        value)
{
  if (obj->time != value)
    {
      obj->time = value;

      g_assert (properties[PROP_TIME] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_TIME]);
    }
}
static void
ts_synapse_set_type (TsSynapse *obj,
                     gint64        value)
{
  if (obj->type != value)
    {
      obj->type = value;

      g_assert (properties[PROP_TYPE] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_TYPE]);
    }
}
static void
ts_synapse_set_status (TsSynapse *obj,
                     gboolean    status)
{
  status = !!status;

  if (obj->status != status)
    {
      obj->status = status;

      g_assert (properties[PROP_STATUS] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_STATUS]);
    }
}

static void
ts_synapse_set_name (TsSynapse  *obj,
                     const gchar *name)
{
  if (g_strcmp0 (obj->name, name) != 0)
    {
      g_free (obj->name);
      obj->name = g_strdup (name);

      g_assert (properties[PROP_NAME] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_NAME]);
    }
}

static void
ts_synapse_set_peer_list (TsSynapse  *obj,
                     gpointer value)
{
  obj->peer_list = value;
}
static void
ts_synapse_set_dna (TsSynapse  *obj,
                     gpointer value)
{
  obj->dna = value;
}
static void
ts_synapse_set_parent (TsSynapse  *obj,
                     gpointer value)
{
  obj->parent = value;
}
static void
ts_synapse_set_son (TsSynapse  *obj,
                     gpointer value)
{
  obj->son = value;
}
static void
ts_synapse_set_next (TsSynapse  *obj,
                     gpointer value)
{
  obj->next = (GObject *) value;
}

static void
ts_synapse_finalize (GObject *gobject)
{
  TsSynapse *self = (TsSynapse *) gobject;

  g_free (self->name);

  g_object_notify (gobject, "port");
  g_object_notify_by_pspec (gobject, properties[PROP_STATUS]);

  G_OBJECT_CLASS (ts_synapse_parent_class)->finalize (gobject);
}

static void
ts_synapse_set_property (GObject *gobject,
                          guint prop_id,
                          const GValue *value,
                          GParamSpec *pspec)
{
  TsSynapse *tobj = (TsSynapse *) gobject;

  g_assert_cmpint (prop_id, !=, 0);
  g_assert_cmpint (prop_id, !=, N_PROPERTIES);
  g_assert (pspec == properties[prop_id]);

  switch (prop_id)
    {
    case PROP_ID:
      ts_synapse_set_id (tobj, g_value_get_int64 (value));
      break;
    case PROP_VALUE:
      ts_synapse_set_value (tobj, g_value_get_int64 (value));
      g_signal_emit_by_name (tobj, "value-changed", tobj->value);
      break;
    case PROP_PROPORTION:
      ts_synapse_set_proportion (tobj, g_value_get_int64 (value));
      break;
    case PROP_DENDRITE_ID:
      ts_synapse_set_dendrite_id (tobj, g_value_get_int64 (value));
      break;

    case PROP_TIME:
      ts_synapse_set_time (tobj, g_value_get_int64 (value));
      break;
    case PROP_TYPE:
      ts_synapse_set_type (tobj, g_value_get_int64 (value));
      break;

    case PROP_STATUS:
      ts_synapse_set_status (tobj, g_value_get_boolean (value));
      break;

    case PROP_NAME:
      ts_synapse_set_name (tobj, g_value_get_string (value));
      break;
    case PROP_DNA:
      ts_synapse_set_dna (tobj, g_value_get_pointer (value));
      break;
    case PROP_PEER_LIST:
      ts_synapse_set_peer_list (tobj, g_value_get_pointer (value));
      break;

    case PROP_PARENT:
      ts_synapse_set_parent (tobj, g_value_get_pointer (value));
      break;
    case PROP_SON:
      ts_synapse_set_son (tobj, g_value_get_pointer (value));
      break;
    case PROP_NEXT:
      ts_synapse_set_next (tobj, g_value_get_pointer (value));
      break;

    default:
      g_assert_not_reached ();
    }
}

static void
ts_synapse_get_property (GObject *gobject,
                          guint prop_id,
                          GValue *value,
                          GParamSpec *pspec)
{
  TsSynapse *tobj = (TsSynapse *) gobject;

  g_assert_cmpint (prop_id, !=, 0);
  g_assert_cmpint (prop_id, !=, N_PROPERTIES);
  g_assert (pspec == properties[prop_id]);

  switch (prop_id)
    {
    case PROP_ID:
      g_value_set_int64 (value, tobj->id);
      break;
    case PROP_VALUE:
      g_value_set_int64 (value, tobj->value);
      break;
    case PROP_PROPORTION:
      g_value_set_int64 (value, tobj->proportion);
      break;
    case PROP_DENDRITE_ID:
      g_value_set_int64 (value, tobj->dendrite_id);
      break;

    case PROP_TIME:
      g_value_set_int64 (value, tobj->time);
      break;
    case PROP_TYPE:
      g_value_set_int64 (value, tobj->type);
      break;

    case PROP_STATUS:
      g_value_set_boolean (value, tobj->status);
      break;

    case PROP_NAME:
      g_value_set_string (value, tobj->name);
      break;

    case PROP_DNA:
      g_value_set_pointer (value, tobj->dna);
      break;
    case PROP_PEER_LIST:
      g_value_set_pointer (value, tobj->peer_list);
      break;

    case PROP_NEXT:
      g_value_set_pointer (value, (gpointer) tobj->next);
      break;
    case PROP_SON:
      g_value_set_pointer (value, tobj->son);
      break;
    case PROP_PARENT:
      g_value_set_pointer (value, tobj->parent);
      break;

    default:
      g_assert_not_reached ();
    }
}

static void value_changed_handler(TsSynapse *self, gint64 value)
{
  //id 1:in 0:out
  g_print("L%d f-%s synapse[%ld] value changed to %ld, type:%"G_GINT64_FORMAT"\n",
      __LINE__, __func__,
      self->id, value, self->type);
  // update time
  GList *l = NULL;
  gint64 tt;

  tt = g_get_real_time ();
  g_object_set (self, "time", tt, NULL);
  if (self->peer_list) {//axon
    l =  self->peer_list;
    while (l) {
      g_object_set (l->data, "value", value * self->proportion, NULL);
      l = l->next;
    }
  } else if (self->type == 1) {//dendrite
    g_signal_emit_by_name (self->parent, "value-changed", value*self->proportion);
  }
}

static void
ts_synapse_init (TsSynapse *self)
{
  g_signal_connect (self, "value-changed", G_CALLBACK(value_changed_handler),
      self);
}
static void
ts_synapse_class_init (TsSynapseClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  properties[PROP_ID] = g_param_spec_int64 ("id", "id", "id",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);
  properties[PROP_VALUE] = g_param_spec_int64 ("value", "value", "value",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);
  properties[PROP_PROPORTION] = g_param_spec_int64 (
      "proportion", "proportion", "proportion",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);
  properties[PROP_DENDRITE_ID] = g_param_spec_int64 (
      "dendrite_id", "dendrite_id", "dendrite_id",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);

  properties[PROP_TIME] = g_param_spec_int64 ("time", "time", "time",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);
  properties[PROP_TYPE] = g_param_spec_int64 ("type", "type", "type",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);
  g_signal_new("value-changed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
      G_STRUCT_OFFSET(TsSynapseClass, value_changed), NULL, NULL, NULL,
      G_TYPE_NONE, 1, G_TYPE_INT64);
  properties[PROP_STATUS] = g_param_spec_int ("status", "Status", "Status",
      -1, G_MAXINT, 0, G_PARAM_READWRITE);
  properties[PROP_NAME] = g_param_spec_string ("name", "Name", "Name",
      NULL, G_PARAM_READWRITE);
  properties[PROP_DNA] = g_param_spec_pointer ("dna", "dna", "dna",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_PEER_LIST] = g_param_spec_pointer (
      "peer_list", "peer_list", "peer_list",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_SON] = g_param_spec_pointer ("son", "son", "son",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_NEXT] = g_param_spec_pointer ("next", "next", "next",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_PARENT] = g_param_spec_pointer ("parent", "parent", "parent",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);

  gobject_class->set_property = ts_synapse_set_property;
  gobject_class->get_property = ts_synapse_get_property;
  gobject_class->finalize = ts_synapse_finalize;

  g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
}

//TsSynapse *ts_synapse_new (void)
TsSynapse *ts_synapse_new (gint64 id, gint64 proportion, gint64 dendrite_id)
{
  TsSynapse *synapse;

  synapse =  g_object_new (ts_synapse_get_type (),
      "id", id, "proportion", proportion, "dendrite_id", dendrite_id, NULL);
  g_debug ("L%d f-%s new synapse id:%ld", __LINE__, __func__, id);
//  synapse->value_change = value_change_handler; 

  return synapse;
}
TsSynapse *ts_synapses_get_object (TsSynapse *gobject,
                          const gchar *name,
                          gpointer value)
{
  TsSynapse *tobj = gobject;
  guint prop_id;
  gboolean found = FALSE;
  if (!g_strcmp0 (name, "id")) {
    prop_id = PROP_ID;
  }
  while (tobj) {
  //g_debug ("%s\tid:%d\n", __func__, tobj->id);
    switch (prop_id)
      {
      case PROP_ID:
        if (tobj->id == *(gint64 *)value) {
          found = TRUE;
        }
        break;
      default:
        g_assert_not_reached ();
      }
    if (found)
      break;
    tobj = (TsSynapse *) tobj->next;
  }
  return tobj;
}
void ts_synapses_add_end (TsSynapse **tp, TsSynapse *ts)
{
  TsSynapse *now = NULL;

  now = *tp;
  if (now) {
    while (now->next) {
      now = (TsSynapse *)(now->next);
    }
    now->next = (GObject *)ts;
  } else {
    *tp = ts;
  }
}
